/**
 * 
 */
package qy.qyalgotrader.view.excel;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Table;

import qy.jalgotrade.bar.Bar.Frequency;
import qy.qyalgotrader.mystrategy.Inspectable;
import qy.qyalgotrader.utils.CommonDataUtils;
import qy.qyalgotrader.utils.Constants;
import qy.qyalgotrader.utils.Services;
import qy.qyalgotrader.utils.apiservice.view.ExcelViewPrx;

/**
 * <pre>
 * 实现指标的展现: 负责定位特定品种所属记录所在行 (Row), 并根据不同的指标分项, 确定展现单元格的列 (Column);
 * 
 * 采用 PULL 模式: 指标更新事件的传递路径:  Handler &lt;-(PULL)- View (&lt;- EventSource).
 * </pre>
 * 
 * @author qy
 *
 */
public class ExcelPullView {

	private static Logger __log = LoggerFactory.getLogger(ExcelPullView.class);

	/**
	 * 排序列:
	 */
	public static final String PARAM_SORT_COLUMN = "sortColumn";

	/**
	 * 序号列:
	 */
	public static final String PARAM_SEQ_COLUMN = "seqColumn";

	/**
	 * 排序方式 (默认值: xlDescending (降序)):
	 */
	public static final String PARAM_SORT_ORDER = "sortOrder";

	/**
	 * 置顶行数 (默认为 5: 上证50, 沪深300, 中证500, 中证1000, 创业板综):
	 */
	public static final String PARAM_RESERVED_TOP_ROWS = "reservedTopRows";

	/**
	 * 排序的范围, 包含排序单元格范围的最小表达式:
	 */
	public static final String PARAM_SORT_RANGE = "sortRange";

	/**
	 * 刷新视图的时间间隔 (秒, 默认为 9 秒):
	 */
	public static final String PARAM_REFRESH_INTERVAL = "refreshInterval";

	/**
	 * 完整数据集边界坐标: North West (左上)
	 */
	public static final String PARAM_DATA_CELL_BORDER_NW = "dataCellBorderNW";

	/**
	 * 完整数据集边界坐标: South West (左下)
	 */
	public static final String PARAM_DATA_CELL_BORDER_SW = "dataCellBorderSW";

	/**
	 * 完整数据集边界坐标: North East (右上)
	 */
	public static final String PARAM_DATA_CELL_BORDER_NE = "dataCellBorderNE";

	/**
	 * 完整数据集边界坐标: South East (右下)
	 */
	public static final String PARAM_DATA_CELL_BORDER_SE = "dataCellBorderSE";

	/**
	 * 排序数据集边界坐标: North West (左上)
	 */
	public static final String PARAM_SORT_DATA_CELL_BORDER_NW = "sortDataCellBorderNW";

	/**
	 * 排序数据集边界坐标: South West (左下)
	 */
	public static final String PARAM_SORT_DATA_CELL_BORDER_SW = "sortDataCellBorderSW";

	/**
	 * 排序数据集边界坐标: North East (右上)
	 */
	public static final String PARAM_SORT_DATA_CELL_BORDER_NE = "sortDataCellBorderNE";

	/**
	 * 排序数据集边界坐标: South East (右下)
	 */
	public static final String PARAM_SORT_DATA_CELL_BORDER_SE = "sortDataCellBorderSE";

	/**
	 * 
	 * @author qy
	 *
	 */
	private class IndicatorPullRunner implements Runnable {

		private boolean __isTradingDay;

		public IndicatorPullRunner() {

			__isTradingDay = CommonDataUtils.isTradingDay(LocalDate.now());
		}

		/*
		 * 
		 */
		@Override
		public void run() {

			__log.info("IndicatorPullRunner thread started.");

			long refreshInterval = Long.parseLong(__excelView.getParam("refreshInterval"));
			while (__excelView.isRunning()) {
				ZonedDateTime dm = ZonedDateTime.now();
				LocalTime tm = dm.toLocalTime();
				__pullAndRender();
				if (__isTradingDay) {
					// 交易日, 若处于连续竞价期间以及边界时间, 则持续地进行定时刷新:
					if (tm.compareTo(Constants.OPEN_TIME_AM_CALL_AUCTION) >= 0
					        && tm.compareTo(Constants.CLOSE_TIME_AM) < 0
					        || tm.compareTo(Constants.OPEN_TIME_PM) >= 0
					                && tm.compareTo(Constants.CLOSE_TIME_PM_CALL_AUCTION) < 0) {
						__pullAndRender();
					}
				} else {
					// 非交易日, 只刷新一次, 然后结束刷新线程的运行:
					__excelView.stop();
				}

				try {
					Thread.sleep(refreshInterval);
				} catch (InterruptedException e) {
					__log.warn("InterruptedException: %s", ExceptionUtils.getStackTrace(e));
				}
			}

			__log.info("IndicatorPullRunner thread finished.");
		}
	}

	private Inspectable __monitoring;

	private ExcelViewPrx __excelView;

	private Thread __iprThread;

	private Frequency __indicatorsSrcFreq;

	/**
	 * 
	 * @param monitoring
	 */
	public ExcelPullView(Inspectable monitoring, Frequency srcFreq) {

		__monitoring = monitoring;
		__excelView = ExcelViewPrx.checkedCast(Services.getInstance().createIceViewProxy("ExcelView"));
		__iprThread = new Thread(new IndicatorPullRunner());
		__indicatorsSrcFreq = srcFreq;
	}

	/**
	 * 
	 * @param keyColName
	 * @param indicatorNameMapping
	 * @param paramsByName
	 */
	public void autoConfigure(String keyColName, Map<String, String> indicatorNameMapping,
	        Map<String, String> paramsByName) {

		__excelView.autoConfigure(keyColName, indicatorNameMapping, paramsByName);
	}

	/**
	 * 
	 */
	public void start() {

		__iprThread.start();
	}

	/**
	 * 
	 * @return
	 */
	public boolean isRunning() {

		return __excelView.isRunning();
	}

	/**
	 * 
	 */
	private void __pullAndRender() {

		List<String> allKeys = Arrays.asList(__excelView.getKeyList());
		Pair<Map<String, LocalDateTime>, Table<String, String, Double>> allDmsIndcs = __monitoring
		        .checkNow(__indicatorsSrcFreq, Optional.of(allKeys), Optional.empty());
		Map<String, LocalDateTime> allDms = allDmsIndcs.getLeft();
		if (__excelView.isRenderable(Constants.DATE)) {
			for (String key : allDms.keySet()) {
				__excelView.render(key, Constants.DATE, allDms.get(key).toLocalDate().format(Constants.DATE_FORMAT_STD),
				        Optional.empty());
			}
		}
		if (__excelView.isRenderable(Constants.TIME)) {
			for (String key : allDms.keySet()) {
				__excelView.render(key, Constants.TIME, allDms.get(key).toLocalTime().format(Constants.TIME_FORMAT_STD),
				        Optional.empty());
			}
		}

		Table<String, String, Double> allIndicators = allDmsIndcs.getRight();
		for (String key : allKeys) {
			Set<String> allIndicatorNames = allIndicators.columnKeySet();
			for (String indicator : allIndicatorNames) {
				if (__excelView.isRenderable(indicator)) {
					__excelView.render(key, indicator, allIndicators.get(key, indicator).toString(), Optional.empty());
				}
			}
		}
		__excelView.refresh();
	}
}
